﻿using FreeRedis;

class Program
{
    static void Main(string[] args)
    {
        //Redis分布式锁方案三：使用Lua脚本(包含SETNX + EXPIRE两条指令)
        //使用Lua脚本来保证原子性（包含setnx和expire两条指令）

        Console.WriteLine("请输入秒杀商品Id：");
        var skuId = Console.ReadLine();

        Console.WriteLine("请输入秒杀商品库存:");
        var skuStockNum = int.Parse(Console.ReadLine() ?? "10");
        RedisClient redisSkuStockClient = new RedisClient("127.0.0.1:32002,defaultDatabase=0");
        var skuStockKey = $"sku:stock:{skuId}";
        redisSkuStockClient.Set<int>(skuStockKey, skuStockNum);

        Console.WriteLine("请输入秒杀数量：");
        var skuNum = int.Parse(Console.ReadLine() ?? "1");

        Console.WriteLine("请输入秒杀并发人数：");
        var userCount = int.Parse(Console.ReadLine() ?? "100");

        var redisKey = $"sale:activity:sku-{skuId}";
        for (int i = 1; i <= userCount; i++)
        {
            var userId = i;
            Thread thread = new Thread(new ThreadStart(() =>
            {
                RedisClient redisSetExClient = new RedisClient("127.0.0.1:32002,defaultDatabase=0");
                var param = new { skuId = skuId, SkuName = $"商品：{skuId}", count = skuNum, userId = userId };
                var paramValue = Newtonsoft.Json.JsonConvert.SerializeObject(param);
            
                string lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + " redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end";
                var keys = new string[1] { redisKey };
                var expireTime = "30";
                var arguments = new string[2] { paramValue, expireTime };
                var lockObject = redisSetExClient.Eval(lua_scripts, keys, arguments);
                if (lockObject.Equals(1L))
                {
                    var skuStock = redisSetExClient.Get<int>(skuStockKey);
                    var newSkuStock = skuStock - skuNum;
                    if (newSkuStock >= 0)
                    {
                        Console.WriteLine($"用户:{userId}，已获取到锁，已生成订单，现库存：{skuStock},下单后库存：{newSkuStock},--------------------");
                        redisSkuStockClient.Set<int>(skuStockKey, newSkuStock);
                        redisSkuStockClient.Del(redisKey);
                    }
                    else
                    {
                        Console.WriteLine($"用户:{userId}，未生成订单，现库存：{skuStock}");
                    }
                }
                else
                {
                    Console.WriteLine($"用户:{userId}，未获取到锁");
                }
            }));
            thread.Start();
        }
    }
}